6 NumPy 数组运算
6.1 引言向量化运算的威力
NumPy的核心优势在于其强大的向量化运算能力。与Python循环相比,NumPy运算在底层使用优化的C/Fortran代码,可以提供数十倍甚至上百倍的性能提升。在金融数据处理中,这种性能优势使得复杂计算成为可能。
理论背景:SIMD并行计算
NumPy的高性能部分源于SIMD(Single Instruction, Multiple Data)指令集: - 单指令: 对多个数据执行相同的操作 - 多数据: 一次处理多个数值 - 并行执行: CPU的多个ALU同时工作
例如,计算两个数组的和,传统循环需要逐个元素相加,而SIMD可以一次性处理4个或更多元素(取决于CPU架构)。
6.2 统计运算
6.2.1 基本统计量
NumPy提供了丰富的统计函数,这些函数可以直接应用于整个数组或指定轴(axis)。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import numpy as np # 导入NumPy数值计算库
# 已知数据
stock_p=np.array([4.98,5.02,4.95,4.91,4.98,4.92,4.88,4.92,4.88,4.82,4.85,4.89,4.91,4.86,4.84,4.92,5.01,5.04])
# 创建NumPy数组stock_return
stock_return=np.array([-0.7968,1.4141,0.8147,-1.4056,1.2195,0.8197,-0.813,0.8197,1.2448,-0.6186,-0.818,-0.4073,1.0288,0.4132,-1.626,-1.7964,-0.5952,-1.5625])
# 1. 计算股票的最高价并输出
p_max=stock_p.max()
print(f"中国建筑8月最高股价为{p_max}") # 输出中国建筑8月最高股价为
# 2. 计算股票的平均涨跌幅,使用round()函数保留三位小数并输出
return_avg=stock_return.mean().round(3)
print(f"平均涨跌幅为{return_avg}") # 输出平均涨跌幅为
# 3. 计算股票涨跌幅的方差和标准差,使用round()函数并保留3位小数
return_var=stock_return.var().round(3)
return_std=stock_return.std().round(3) # 计算标准差并四舍五入
print(f"涨跌幅的方差{return_var},标准差{return_std}") # 输出涨跌幅的方差代码深度解析:
.max()和.min():- 返回数组的最大/最小值
- 支持
axis参数指定沿哪个轴计算 - 等价于
np.max(arr)和np.min(arr)
.mean()(算术平均):mean = Σx_i / n- 受极端值影响
- 适用于对称分布数据
.var()(方差):# 样本方差(默认ddof=0) var = Σ(x_i - mean)² / n # 总体方差(ddof=1) var_pop = Σ(x_i - mean)² / (n-1)ddof: Delta Degrees of Freedom(自由度调整)- 金融分析常用样本方差(ddof=1)
.std()(标准差):std = √variance- 与原始数据同单位,更易解释
- 金融含义: 波动率的度量,风险的核心指标
6.2.2 多维数组的统计运算
# =============================================================================
# 题目:沿指定轴计算统计量
# =============================================================================
# 本任务演示如何对多维数组沿指定轴进行统计运算
# 场景:分析多只股票多日的收益率数据,计算不同维度的统计量
# ==================== 创建多维数据 ====================
# 多只股票多日收益率:3只股票 × 4个交易日的收益率矩阵
# 每行代表一只股票,每列代表一个交易日
returns = np.array([
[0.05, 0.03, -0.02, 0.04], # 股票A:4个交易日的收益率
[0.02, 0.01, 0.03, -0.01], # 股票B:4个交易日的收益率
[-0.01, 0.02, 0.01, 0.05] # 股票C:4个交易日的收益率
]) # 形状为(3, 4)的二维数组,3行4列
# ==================== 沿axis=0计算每日的平均收益率 ====================
# axis=0:沿垂直方向(跨行)计算,即对每列计算均值
# 这里跨3只股票计算每个交易日的市场平均收益率
daily_avg = returns.mean(axis=0) # 对每列求均值,返回长度为4的一维数组
print(f"每日平均收益率: {daily_avg}")
# 输出解读:返回4个值,分别对应4个交易日的市场平均收益率
# 金融含义:市场平均收益率反映整体市场的表现,可用于计算市场因子
# 应用:如果某日市场平均收益率为正,说明当日市场整体上涨
# ==================== 沿axis=1计算每只股票的平均收益率 ====================
# axis=1:沿水平方向(跨列)计算,即对每行计算均值
# 这里跨4个交易日计算每只股票的平均收益率
stock_avg = returns.mean(axis=1) # 对每行求均值,返回长度为3的一维数组
print(f"每只股票平均收益率: {stock_avg}")
# 输出解读:返回3个值,分别对应股票A、B、C的平均收益率
# 金融含义:个股平均收益率反映该股票在统计期内的典型表现
# 应用:比较不同股票的平均收益率,筛选表现较好的股票
# ==================== 计算累积收益率 ====================
# .cumsum(axis=1):沿axis=1方向计算累积和
# 累积和用于计算多期收益率的累积效果
cumulative_returns = returns.cumsum(axis=1) # 对每行计算累积和,返回形状为(3, 4)的数组
print(f"\n累积收益率:\n{cumulative_returns}")
# 输出解读:每行的第i个值 = 该行前i个元素的和
# 例如股票A的第3个值 = 0.05 + 0.03 + (-0.02) = 0.06,表示前3日累积收益率为6%
# 金融含义:累积收益率反映投资一段时间的总收益
# 应用:计算持仓的累积收益,评估投资策略的历史表现axis参数理解: - axis=0: 垂直方向(跨行),对每列计算 - axis=1: 水平方向(跨列),对每行计算 - axis=None: 对所有元素计算(默认)
金融应用: - axis=0: 多只股票同一时间的表现 - axis=1: 单只股票的时间序列特征
6.3 数学运算
6.3.1 算术运算
NumPy支持元素级的算术运算,这是向量化的核心。
# =============================================================================
# 题目:NumPy数组的算术运算
# =============================================================================
# 本任务演示NumPy数组的元素级算术运算
# 场景:金融场景中的价格调整、价差计算、持仓价值计算、收益率计算
# ==================== 创建基础数据 ====================
# 基础算术:创建股价序列
prices = np.array([10, 20, 30, 40, 50]) # 5个交易日的股价数据,单位:元
# ==================== 加法:价格调整 ====================
# 创建调整数组,模拟价格调整(如分红、拆股等)
adjustment = np.array([1, 2, 3, 4, 5]) # 每天的价格调整幅度
new_prices = prices + adjustment # 对应元素相加,得到调整后的价格
print(f"调整后价格: {new_prices}")
# 输出解读:[11, 22, 33, 44, 55],每个元素 = 对应位置的price + adjustment
# 金融应用:股票除权除息后的价格调整,或者计算复权价格
# ==================== 减法:价差计算 ====================
# prices[1:]:从索引1开始到最后的切片,即[20, 30, 40, 50]
# prices[:-1]:从开始到倒数第二个的切片,即[10, 20, 30, 40]
# 相减得到相邻两天的价格差
price_diff = prices[1:] - prices[:-1] # 计算相邻价格的差值
print(f"价格变化: {price_diff}")
# 输出解读:[10, 10, 10, 10],表示每天价格上涨10元
# 金融含义:价格差反映价格的绝对变化,用于计算价格动量
# 应用:技术分析中的价格变化率、MACD指标等
# ==================== 乘法:计算持仓价值 ====================
# 创建持仓数量数组
shares = np.array([100, 200, 150]) # 3只股票的持仓数量
# prices[:3]:取prices的前3个元素[10, 20, 30]
position_value = prices[:3] * shares # 对应元素相乘,计算每只股票的持仓价值
print(f"\n持仓价值: {position_value}")
# 输出解读:[1000, 4000, 4500],分别表示3只股票的持仓价值
# 金融应用:持仓价值 = 股价 × 持仓数量,用于计算投资组合的市值
# 注意:两个数组形状必须相同,或可以广播
# ==================== 除法:计算收益率 ====================
# 简单收益率公式:return = (P_t - P_{t-1}) / P_{t-1}
# prices[1:]:当期价格,prices[:-1]:上期价格
returns = (prices[1:] - prices[:-1]) / prices[:-1] # 计算日收益率
print(f"\n简单收益率: {returns}")
# 输出解读:[1.0, 0.5, 0.33, 0.25],分别表示每天的收益率(小数形式)
# 金融含义:第1天收益率 = (20-10)/10 = 100%,第2天 = (30-20)/20 = 50%
# 应用:计算历史收益率,用于评估股票的历史表现和风险
# ==================== 幂运算:复利计算 ====================
# np.arange(1, 6):生成整数序列[1, 2, 3, 4, 5],代表年数
# (1 + 0.05):1 + 年化收益率5%
# **:幂运算符,计算复利因子
compound = (1 + 0.05) ** np.arange(1, 6) # 计算1到5年的复利因子
print(f"\n5年复利因子: {compound}")
# 输出解读:[1.05, 1.1025, 1.1576, 1.2155, 1.2763]
# 金融含义:第1年末本金变为1.05倍,第2年末变为1.1025倍,依此类推
# 应用:复利计算、未来价值计算、投资回报预测运算符对应关系:
| 运算符 | NumPy函数 | 描述 |
|---|---|---|
+ |
np.add |
加法 |
- |
np.subtract |
减法 |
* |
np.multiply |
乘法 |
/ |
np.divide |
除法 |
// |
np.floor_divide |
整除 |
% |
np.mod |
取模 |
** |
np.power |
幂运算 |
6.3.2 数学函数
NumPy提供了完整的数学函数库,这些函数都是向量化的。
# =============================================================================
# 题目:NumPy数学函数在金融计算中的应用
# =============================================================================
# 本任务演示NumPy常用数学函数在金融场景中的应用
# 场景:对数收益率计算、复利计算、波动率计算、价格取整
# ==================== 创建示例数据 ====================
# 示例数据:5个交易日的股价数据
prices = np.array([100, 105, 98, 102, 110]) # 股价序列,单位:元
# ==================== 1. 对数收益率计算(金融常用) ====================
# np.log(prices):计算价格的自然对数,ln(prices)
# np.diff():计算相邻元素的差值,a[n] - a[n-1]
# 对数收益率公式:r_log = ln(P_t) - ln(P_{t-1}) = ln(P_t / P_{t-1})
log_returns = np.diff(np.log(prices)) # 计算对数收益率
print(f"对数收益率: {log_returns}")
# 输出解读:返回4个值,表示4个交易日的对数收益率
# 金融含义:对数收益率具有时间可加性,多期收益率可以直接相加
# 优势:相比简单收益率,对数收益率更接近正态分布,便于统计建模
# 应用:连续复利模型、Black-Scholes期权定价模型
# ==================== 2. 指数函数:复利计算 ====================
# 创建年化收益率数组:4只股票的年收益率
annual_returns = np.array([0.05, 0.06, 0.04, 0.07]) # 小数形式,5%、6%、4%、7%
# annual_returns * 5:计算5年的累积收益率
# np.exp():指数函数,e^x,用于连续复利计算
# cumulative_5yr = e^(r * 5) - 1,连续复利公式
cumulative_5yr = np.exp(annual_returns * 5) - 1 # 计算5年累计收益率
print(f"\n5年累计收益率: {cumulative_5yr}")
# 输出解读:返回4个值,表示4只股票5年后的累计收益率
# 金融含义:假设年化收益率为5%,5年后累计收益率 = e^(0.05*5) - 1 = 28.4%
# 应用:长期投资回报预测、养老金计算、财富增长规划
# ==================== 3. 平方和平方根:波动率计算 ====================
# np.std():计算标准差,衡量收益率波动程度
volatility = np.std(annual_returns) # 计算年化收益率的标准差
# ** 2:平方运算,计算方差
variance = volatility ** 2 # 方差 = 标准差的平方
print(f"\n波动率: {volatility:.4f}")
print(f"方差: {variance:.4f}")
# 输出解读:波动率如0.0112表示年收益率的标准差为1.12个百分点
# 金融含义:波动率是风险的量化度量,波动率越大,风险越高
# 应用:投资组合风险管理、VaR(在险价值)计算、期权定价
# ==================== 4. 绝对值:计算收益的绝对变化 ====================
# np.diff(prices):计算相邻价格的差值
# np.abs():计算绝对值,将负数转为正数
abs_changes = np.abs(np.diff(prices)) # 计算价格变化的绝对值
print(f"\n价格绝对变化: {abs_changes}")
# 输出解读:返回正数,表示价格变化的幅度(不考虑方向)
# 金融应用:计算价格波动幅度,不考虑涨跌方向
# 区别:price_diff有正负(方向),abs_changes只有大小(幅度)
# ==================== 5. 取整函数 ====================
# 创建浮点数价格数组,模拟包含小数的股价
prices_float = np.array([100.56, 105.23, 98.87]) # 股价保留两位小数
# np.floor():向下取整,返回不大于x的最大整数
print(f"\n向下取整: {np.floor(prices_float)}")
# 输出解读:[100., 105., 98.],每个元素向下取整
# 应用:金融计算中需要保守估计时使用向下取整
# np.ceil():向上取整,返回不小于x的最小整数
print(f"向上取整: {np.ceil(prices_float)}")
# 输出解读:[101., 106., 99.],每个元素向上取整
# 应用:计算所需资金时向上取整,确保资金充足
# np.round():四舍五入到最接近的整数
print(f"四舍五入: {np.round(prices_float)}")
# 输出解读:[101., 105., 99.],按照四舍五入规则取整
# 应用:报价、显示价格时使用四舍五入
# 注意:round(0.5) = 0,采用"银行家舍入法"(向偶数舍入)补充说明:对数收益率的数学基础
在金融中,对数收益率(Log Return)优于简单收益率:
\[ r_{log} = \ln\left(\frac{P_t}{P_{t-1}}\right) = \ln(P_t) - \ln(P_{t-1}) \]
优势: 1. 时间可加性: 多期收益率可以直接相加 \[ r_{log,1→3} = r_{log,1→2} + r_{log,2→3} \] 2. 对称性: 正负收益对称(-50%和+50%的对数收益绝对值相等) 3. 正态性: 更接近正态分布,便于统计推断
6.4 金融指标计算实例
6.4.1 案例股票风险收益分析
# =============================================================================
# 题目:综合运用NumPy进行股票风险收益分析
# =============================================================================
# 本任务综合运用NumPy的统计和运算函数,对股票进行全面的风险收益分析
# 场景:分析中国建筑某月的股价数据,计算各类风险和收益指标
# ==================== 创建股价数据 ====================
# 中国建筑8月股价数据(单位:元),共18个交易日
stock_p = np.array([4.98, 5.02, 4.95, 4.91, 4.98, 4.92, 4.88, 4.92,
4.88, 4.82, 4.85, 4.89, 4.91, 4.86, 4.84, 4.92,
5.01, 5.04]) # 一维数组,存储每日收盘价
# ==================== 计算日收益率 ====================
# stock_p[1:]:从第2个价格开始(第2天到第18天的价格)
# stock_p[:-1]:从第1个价格到倒数第2个(第1天到第17天的价格)
# 收益率公式:(当期价格 - 上期价格) / 上期价格
daily_returns = (stock_p[1:] - stock_p[:-1]) / stock_p[:-1] # 计算17个日收益率
# 输出解读:返回长度为17的数组,表示第2天到第18天的日收益率
# 金融含义:正收益率表示当天上涨,负收益率表示当天下跌
# ==================== 计算基本统计量 ====================
# .mean():计算日平均收益率
mean_return = daily_returns.mean() # 17个日收益率的算术平均
# 金融含义:表示平均每日的收益率水平,用于估计未来预期收益
# .std():计算日收益率的标准差
std_return = daily_returns.std() # 日波动率
# 金融含义:表示日收益率围绕均值波动的程度,是日风险的度量
# 年化波动率:将日波动率转换为年化波动率
# np.sqrt(252):根号252,252是一年的交易日数
# 公式:年化波动率 = 日波动率 × sqrt(252)
annualized_vol = std_return * np.sqrt(252) # 计算年化波动率
# 金融含义:假设日波动率持续一年,年化波动率约为多少
# 应用:不同资产的波动率比较、风险预算、风险控制
# ==================== 打印风险收益分析报告 ====================
print("=" * 50) # 打印分隔线
print("中国建筑8月风险收益分析") # 报告标题
print("=" * 50) # 打印分隔线
# 打印收益率统计信息
print(f"\n【收益率统计】")
print(f" 日平均收益率: {mean_return:.4%}") # 格式化为百分比,保留4位小数
print(f" 日收益率标准差: {std_return:.4%}") # 日波动率
print(f" 年化波动率: {annualized_vol:.4%}") # 年化波动率
# 输出解读:如日平均收益率-0.10%表示平均每日下跌0.10%
# 金融含义:负的平均收益率表明该月整体表现不佳
# ==================== 计算价格统计 ====================
# .max():计算最高价
max_price = stock_p.max() # 8月期间的最高股价
# .min():计算最低价
min_price = stock_p.min() # 8月期间的最低股价
# 价格区间:最高价 - 最低价
price_range = max_price - min_price # 计算8月价格波动区间
print(f"\n【价格统计】")
print(f" 最高价: {max_price:.2f}元") # 保留2位小数
print(f" 最低价: {min_price:.2f}元") # 保留2位小数
print(f" 价格区间: {price_range:.2f}元") # 价格波动幅度
# 金融应用:根据价格区间设置止损止盈位,如最低价附近设置止损
# ==================== 计算下行风险指标 ====================
# 布尔索引:筛选收益率为负的交易日
daily_returns < 0 # 返回布尔数组,True表示该日收益率为负
downside_returns = daily_returns[daily_returns < 0] # 提取所有负收益率
# 金融含义:下跌日的收益率数据,用于计算下行风险
# 下行波动率:只考虑下跌日的波动率
downside_risk = downside_returns.std() * np.sqrt(252) # 年化下行波动率
# 金融含义:下行风险只关注下跌时的波动,更符合投资者对风险的感知
print(f"\n【风险指标】")
print(f" 下跌日波动率: {downside_risk:.4%}") # 下行风险
# 输出解读:下行风险通常小于整体波动率,因为忽略了上涨日的波动
# 夏普比率:衡量单位风险的超额收益
# 公式:夏普比率 = (年化收益率 - 无风险利率) / 年化波动率
# mean_return * 252:日收益率年化
# 0.03:假设无风险利率为3%
sharpe_ratio = (mean_return * 252 - 0.03) / annualized_vol
print(f" 夏普比率(假设无风险利率3%): {sharpe_ratio:.2f}")
# 输出解读:夏普比率如-0.5,表示每承担1单位风险,超额收益为-0.5%
# 金融含义:夏普比率 > 1 为优秀,< 1 为一般,< 0 为较差(负值表示回报低于无风险利率)
# 应用:比较不同投资策略的风险调整后收益,筛选性价比高的投资6.5 集合运算
# =============================================================================
# 题目:NumPy集合运算在股票筛选中的应用
# =============================================================================
# 本任务演示NumPy的集合运算函数
# 场景:分析两个投资组合的持仓差异,找出共同持仓和独有持仓
# ==================== 创建投资组合数据 ====================
# 投资组合A的股票代码:包含4只股票
portfolio_a = np.array(['600519.SH', '000858.SZ', '600036.SH', '000002.SZ'])
# 投资组合B的股票代码:包含4只股票
portfolio_b = np.array(['600036.SH', '601318.SH', '000858.SZ', '600000.SH'])
# 注意:股票代码格式,上海交易所.SH,深圳交易所.SZ
# ==================== 交集:找出共同持仓 ====================
# np.intersect1d():计算两个数组的交集,返回共有的元素(已排序)
common = np.intersect1d(portfolio_a, portfolio_b) # 找出A和B都有的股票
print(f"共同持仓: {common}")
# 输出解读:['000858.SZ' '600036.SH'],两只组合共同持有这两只股票
# 金融应用:投资组合重叠度分析,避免过度集中风险
# ==================== 并集:找出所有股票 ====================
# np.union1d():计算两个数组的并集,返回所有不重复的元素(已排序)
all_stocks = np.union1d(portfolio_a, portfolio_b) # 合并A和B的所有股票,去重
print(f"所有股票: {all_stocks}")
# 输出解读:返回6只股票(4+4-2=6),因为2只是共同的
# 金融应用:构建分散化的投资池,确保股票多样性
# ==================== 差集:找出独有持仓 ====================
# np.setdiff1d(ar1, ar2):计算在ar1中但不在ar2中的元素
# 找出A组合独有(在A中但不在B中)的股票
unique_a = np.setdiff1d(portfolio_a, portfolio_b) # A特有的股票
print(f"A组合独有: {unique_a}")
# 输出解读:['000002.SZ' '600519.SH'],这两只只在A组合中
# 找出B组合独有(在B中但不在A中)的股票
unique_b = np.setdiff1d(portfolio_b, portfolio_a) # B特有的股票
print(f"B组合独有: {unique_b}")
# 输出解读:['600000.SH' '601318.SH'],这两只只在B组合中
# 金融应用:策略差异分析,了解不同投资经理的选股偏好6.6 比较运算与布尔索引
# =============================================================================
# 题目:NumPy比较运算与条件筛选
# =============================================================================
# 本任务演示NumPy的比较运算和布尔索引功能
# 场景:根据股价条件筛选股票,找出符合特定标准的股票
# ==================== 创建股价数据 ====================
# 股价数据:5只股票的最新价格
prices = np.array([10.5, 20.3, 15.8, 25.2, 18.6]) # 单位:元
# ==================== 比较运算 ====================
# >:比较运算符,返回布尔数组(True/False)
# 比较每个元素是否大于20
above_20 = prices > 20 # 返回[False, False, False, True, False]
print(f"价格>20元: {above_20}")
# 输出解读:只有第4只股票(25.2元)的价格大于20元,对应位置为True
# 金融应用:筛选高价股、低价股,设置价格阈值
# ==================== 布尔索引 ====================
# 布尔索引:用布尔数组作为索引,提取True位置的元素
# prices > 20:返回布尔数组[False, False, False, True, False]
# prices[布尔数组]:只保留True位置的元素
expensive_stocks = prices[prices > 20] # 筛选价格大于20的股票
print(f"高价股票: {expensive_stocks}")
# 输出解读:[25.2],只有1只股票价格大于20元
# 金融应用:从股票池中筛选符合条件的股票,如PE < 20、市值 > 100亿
# ==================== 多条件筛选 ====================
# &:逻辑与运算符,两个条件同时满足时为True
# (prices >= 15):价格大于等于15
# (prices <= 25):价格小于等于25
# 两个条件用括号括起来,因为&的优先级高于比较运算符
filtered = prices[(prices >= 15) & (prices <= 25)] # 筛选价格在15-25之间的股票
print(f"价格在15-25元之间: {filtered}")
# 输出解读:[15.8, 20.3, 18.6],3只股票的价格在15-25元之间
# 金融含义:价格区间筛选,寻找合理估值的股票
# 应用:价值投资策略,筛选股价处于合理区间的股票
# ==================== 逻辑运算符说明 ====================
# &:逻辑与(AND),两个条件都为True时结果为True
# |:逻辑或(OR),至少一个条件为True时结果为True
# ~:逻辑非(NOT),True变False,False变True
# 注意:必须用括号,因为位运算符优先级高于比较运算符
# 例如:prices > 15 & prices < 25 会报错,应写成 (prices > 15) & (prices < 25)6.7 排序与搜索
# =============================================================================
# 题目:NumPy排序与搜索操作
# =============================================================================
# 本任务演示NumPy的排序和搜索功能
# 场景:对收益率排序,找出最佳和最差表现,以及条件替换
# ==================== 创建无序数据 ====================
# 无序数据:6个交易日的收益率
returns = np.array([0.05, -0.02, 0.03, 0.07, -0.01, 0.04])
# ==================== 排序 ====================
# np.sort():返回排序后的新数组,原数组不变
# 默认升序排列(从小到大)
sorted_returns = np.sort(returns) # 返回排序后的数组
print(f"排序后: {sorted_returns}")
# 输出解读:[-0.02, -0.01, 0.03, 0.04, 0.05, 0.07]
# 金融应用:找出收益率的中位数、分位数,分析收益分布
# ==================== 原地排序 ====================
# .copy():复制数组,避免修改原数组
returns_copy = returns.copy() # 创建returns的副本
# .sort():原地排序,直接修改数组本身,不返回新数组
returns_copy.sort() # 对副本进行原地排序
print(f"原地排序: {returns_copy}")
# 输出解读:returns_copy被排序,returns保持不变
# 性能优势:原地排序不需要额外内存,适用于大数据集
# ==================== argsort:返回排序索引 ====================
# np.argsort():返回排序后的索引数组,而不是排序后的值
# 索引数组表示原始数组中第几个元素应该排在第几位
indices = np.argsort(returns) # 返回索引数组
print(f"\n排序索引: {indices}")
# 输出解读:[1, 4, 2, 5, 0, 3]
# 解释:
# - 索引1的元素(-0.02)应该排在第1位
# - 索引4的元素(-0.01)应该排在第2位
# - 索引0的元素(0.05)应该排在第5位
# - 索引3的元素(0.07)应该排在第6位
# 使用索引数组重建排序后的数组
print(f"按索引重建: {returns[indices]}")
# 金融应用:找出收益率最高和最低的交易日,分析市场时机
# ==================== 搜索:极值位置 ====================
# np.argmax():返回最大值的索引位置
max_idx = np.argmax(returns) # 找出最大值的索引
# np.argmin():返回最小值的索引位置
min_idx = np.argmin(returns) # 找出最小值的索引
print(f"\n最大值位置: {max_idx}, 值: {returns[max_idx]}")
print(f"最小值位置: {min_idx}, 值: {returns[min_idx]}")
# 输出解读:
# - 最大值位置3,值0.07(第4个元素,索引从0开始)
# - 最小值位置1,值-0.02(第2个元素)
# 金融应用:找出最佳和最差交易日,复盘交易决策
# ==================== where:条件选择 ====================
# np.where(condition, x, y):三目运算符
# - condition:布尔条件
# - x:条件为True时返回的值
# - y:条件为False时返回的值
# 将负收益替换为0,正收益保持不变
adjusted = np.where(returns > 0, returns, 0) # 三元运算
print(f"\n负收益调整为0: {adjusted}")
# 输出解读:[0.05, 0, 0.03, 0.07, 0, 0.04]
# 解释:-0.02和-0.01被替换为0,其他保持不变
# 金融应用:止损策略(限制亏损)、收益计算(只计算正收益)6.8 性能优化建议
# =============================================================================
# 题目:NumPy性能优化最佳实践
# =============================================================================
# 本任务演示NumPy性能优化的关键技巧
# 场景:对比Python循环和NumPy向量化的性能差异,展示优化方法
# ==================== 导入必要的库 ====================
import time # 导入time模块,用于计时
# ==================== 创建大规模测试数据 ====================
n = 1000000 # 数据规模:100万元素
arr1 = np.random.randn(n) # 生成100万个标准正态分布随机数
arr2 = np.random.randn(n) # 再生成100万个标准正态分布随机数
# np.random.randn(n):生成从标准正态分布(均值0,标准差1)中抽取的n个随机数
# ==================== 1. 使用NumPy内置函数而非Python循环 ====================
# 慢速方法:使用Python循环逐个元素相加
start = time.time() # 记录开始时间
result_slow = [] # 创建空列表存储结果
for i in range(n): # 循环100万次
result_slow.append(arr1[i] + arr2[i]) # 逐个元素相加并添加到列表
slow_time = time.time() - start # 计算耗时(当前时间 - 开始时间)
# 快速方法:使用NumPy向量化运算
start = time.time() # 记录开始时间
result_fast = arr1 + arr2 # 直接对两个数组相加,一次性完成所有运算
fast_time = time.time() - start # 计算耗时
# ==================== 打印性能对比结果 ====================
print(f"循环时间: {slow_time:.4f}秒") # 显示循环耗时
print(f"向量化时间: {fast_time:.4f}秒") # 显示向量化耗时
print(f"加速比: {slow_time/fast_time:.1f}倍") # 计算加速比(循环时间 / 向量化时间)
# 输出解读:加速比通常在50-200倍之间,取决于CPU和数据规模
# 性能优势原因:
# 1. NumPy底层使用C语言,避免了Python解释器开销
# 2. SIMD指令集并行处理多个数据
# 3. 连续内存访问,提高缓存命中率
# 金融应用:大规模数据处理(如tick数据、高频交易数据)必须使用向量化
# ==================== 2. 就地操作节省内存 ====================
arr_large = np.random.randn(10000) # 创建包含10000个元素的数组
# 慢速方法:创建新数组(需要额外内存分配)
result1 = arr_large * 2 # 创建一个新数组存储结果,原数组不变
# 内存消耗:原数组(80KB) + 新数组(80KB) = 160KB
# 快速方法:原地修改(不需要额外内存)
arr_large *= 2 # 使用*=运算符,直接在原数组上修改,不创建新数组
# 内存消耗:原数组(80KB),节省50%内存
# 性能优势:避免内存分配和复制,速度更快
# 注意:就地操作会修改原数组,如果需要保留原数据应先备份
# ==================== 3. 预分配数组 ====================
# 慢速方法:动态增长列表(频繁内存分配)
result = [] # 创建空列表
for i in range(10000): # 循环10000次
result.append(i ** 2) # 每次追加元素,可能触发内存重新分配
result = np.array(result) # 最后转换为NumPy数组
# 性能问题:
# 1. 列表append可能需要多次内存重新分配
# 2. 最终转换需要复制所有数据
# 快速方法:预分配数组(一次性内存分配)
result = np.empty(10000) # 预先分配10000个元素的空数组(未初始化)
for i in range(10000): # 循环10000次
result[i] = i ** 2 # 直接填充预分配的数组,无需重新分配内存
# 性能优势:
# 1. 一次性分配所有内存,避免多次分配
# 2. 直接写入数组,无需列表到数组的转换
# 金融应用:生成大规模模拟数据、历史数据回测